﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Threading;
using HiLand.Utility.Setting;

namespace HiLand.Utility.Enums.OP
{
    /// <summary>
    ///   Wrapper a data source object for enumerator <paramref name="EnumType"/>.
    /// </summary>
    /// <typeparam name="EnumType">
    ///   The Enum type.
    /// </typeparam>
    /// <exception cref="NotSupportedException"/>
    /// <example>
    /// this.comboBox1.DataSource = new EnumDataSource&lt;MyEnum&gt;();
    /// this.comboBox1.DisplayMember = "DisplayValue";
    /// this.comboBox1.ValueMember = "Value";
    /// </example>
    public class EnumDataSource<EnumType> : List<EnumDataSource<EnumType>.EnumAdapter>
    {
        /// <summary>
        ///   Constructor a new <see cref="EnumDataSource"/>
        /// </summary>
        /// <exception cref="NotSupportedException"/>
        public EnumDataSource():this(string.Empty)
        {
            
        }

        public EnumDataSource(string displaySerialName)
        {
            if (!typeof(EnumType).IsEnum)
            {
                throw new NotSupportedException("Can not support type: " + typeof(EnumType).FullName);
            }

            // Use Enum helper enumerator list all enum values and add to current context.
            foreach (EnumType value in Enum.GetValues(typeof(EnumType)))
            {
                base.Add(new EnumAdapter(value,displaySerialName));
            }
        }

        /// <summary>
        ///   Enum value adapter, used to get values from each Cultures.
        /// </summary>
        public sealed class EnumAdapter
        {
            /// <summary>
            ///   Storage the actual Enum value.
            /// </summary>
            private EnumType _value;

            /// <summary>
            /// 在一个枚举项上，可能有多个Attrbute描述（有某种语言的，可能也有其他的），此项指定要显示哪一个
            /// </summary>
            private string displaySerialName = string.Empty;

            /// <summary>
            ///   Constructor an <see cref="EnumAdapter"/>.
            /// </summary>
            /// <param name="value">The enum value.</param>
            /// <exception cref="">
            ///   
            /// </exception>
            public EnumAdapter(EnumType value)
            {
                if (!Enum.IsDefined(typeof(EnumType), value))
                {
                    throw new ArgumentException(string.Format("{0} is not defined in {1}", value, typeof(EnumType).Name), "value");
                }
                _value = value;
            }

            public EnumAdapter(EnumType value, string displaySerialName)
            {
                if (!Enum.IsDefined(typeof(EnumType), value))
                {
                    throw new ArgumentException(string.Format("{0} is not defined in {1}", value, typeof(EnumType).Name), "value");
                }
                _value = value;
                this.displaySerialName = displaySerialName;
            }

            /// <summary>
            ///   Gets the actual enum value.
            /// </summary>
            public EnumType Value
            {
                get { return _value; }
            }

            /// <summary>
            ///   Gets the display value for enum value by search local resource with currrent UI culture 
            ///   and special key which is concated from Enum type name and Enum value name.
            /// </summary>
            /// <remarks>
            ///   This would get correct display value by accessing location resource with current UI Culture.
            /// </remarks>
            public string DisplayValue
            {
                get
                {
                    Dictionary<string, EnumItemDescriptionAttribute> disc =
                        EnumItemDescriptionAttribute.GetDisplayValues(_value, typeof(EnumType));

                    CultureInfo currentUICulute = Thread.CurrentThread.CurrentUICulture;
                    
                    if (string.IsNullOrEmpty(this.displaySerialName))
                    {
                        this.displaySerialName = Config.GetAppSetting("currentLanguage");
                    }

                    if (string.IsNullOrEmpty(this.displaySerialName))
                    {
                        this.displaySerialName = currentUICulute.Name;
                    }

                    if (disc.ContainsKey(displaySerialName))
                    {
                        return disc[displaySerialName].Description;
                    }
                    return _value.ToString();
                }
            }

            
        }

        /// <summary>
        ///   Define the item of special enum used attribute, you can use it mulitple for multi-descript one enum item.
        /// </summary>
        //[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
        //public class EnumItemDescriptionAttribute : System.Attribute
        //{
            ///**/
            ///// <summary>
            /////   A cache store, because accessing Attribute is according to reflection, it cost too much
            /////   So I add a cache, which based on special enum Type.
            ///// </summary>
            //private static readonly Dictionary<Type, Dictionary<object, Dictionary<CultureInfo, EnumItemDescriptionAttribute>>> _cache =
            //    new Dictionary<Type, Dictionary<object, Dictionary<CultureInfo, EnumItemDescriptionAttribute>>>();
            ///**/
            ///// <summary>
            /////   the culture of Description
            ///// </summary>
            //private CultureInfo _culture;
            ///**/
            ///// <summary>
            /////   the description content.
            ///// </summary>
            //private string _description;

            ///**/
            ///// <summary>
            /////   Constructor a new <see cref="EnumItemDescriptionAttribute"/> with special culture name and description.
            ///// </summary>
            ///// <param name="cultureName">the name of special culture, like en-US, zh-CN etc.</param>
            ///// <param name="description">the content of Descroption</param>
            //public EnumItemDescriptionAttribute(string cultureName, string description)
            //    : this(CultureInfo.GetCultureInfo(cultureName), description)
            //{
            //}

            ///**/
            ///// <summary>
            /////   Constructor a new <see cref="EnumItemDescriptionAttribute"/> with special culture id and description.
            ///// </summary>
            ///// <param name="cultureId">the ID of special culture, like 1033 etc.</param>
            ///// <param name="description">the content of Descroption</param>
            //public EnumItemDescriptionAttribute(int cultureId, string description)
            //    : this(CultureInfo.GetCultureInfo(cultureId), description)
            //{
            //}

            ///**/
            ///// <summary>
            /////   Internal constructor.
            ///// </summary>
            ///// <param name="culture">special <see cref="CultureInfo"/></param>
            ///// <param name="description">the content of Descroption</param>
            //private EnumItemDescriptionAttribute(CultureInfo culture, string description)
            //{
            //    _culture = culture;
            //    _description = description;
            //}

            ///**/
            ///// <summary>
            /////   Gets the <see cref="CultureInfo"/> for current decription.
            ///// </summary>
            //public CultureInfo Culture
            //{
            //    get { return _culture; }
            //}

            ///**/
            ///// <summary>
            /////   Gets the description content.
            ///// </summary>
            //public string Description
            //{
            //    get { return _description; }
            //}

            ///**/
            ///// <summary>
            /////   Gets all <see cref="EnumItemDescriptionAttribute"/>
            /////   for special enum value: <paramref name="value"/> and special enum type: <paramref name="enumType"/>.
            ///// </summary>
            ///// <param name="value">the value of special enum</param>
            ///// <param name="enumType">the enum type of special enum</param>
            ///// <returns>All attributes attatched on special enum value.</returns>
            //public static Dictionary<CultureInfo, EnumItemDescriptionAttribute> GetDisplayValues(object value, Type enumType)
            //{
            //    if (enumType == null || !enumType.IsEnum)
            //    {
            //        throw new NotSupportedException("enumType is not a Enum");
            //    }
            //    if (value == null || !Enum.IsDefined(enumType, value))
            //    {
            //        throw new ArgumentException("value is not defined in " + enumType.FullName);
            //    }

            //    return GetDisplayValuesImp(value, enumType);
            //}

            ///**/
            ///// <summary>
            /////   Gets the <see cref="EnumItemDescriptionAttribute"/> for special enum value: <paramref name="value"/>,
            /////   special enum type: <paramref name="enumType"/> and special culture.
            ///// </summary>
            ///// <param name="value">the value of special enum</param>
            ///// <param name="enumType">the enum type of special enum</param>
            ///// <param name="culture">special culture for item description.</param>
            ///// <returns>The attributes attatched on special enum value.</returns>
            //public static string GetDisplayValue(object value, Type enumType, CultureInfo culture)
            //{
            //    if (enumType == null || !enumType.IsEnum)
            //    {
            //        throw new NotSupportedException("enumType is not a Enum");
            //    }
            //    if (value == null || !Enum.IsDefined(enumType, value))
            //    {
            //        throw new ArgumentException("value is not defined in " + enumType.FullName);
            //    }

            //    if (culture == null)
            //        return value.ToString();

            //    Dictionary<CultureInfo, EnumItemDescriptionAttribute> disc = GetDisplayValuesImp(value, enumType);
            //    if (disc.ContainsKey(culture))
            //        return disc[culture].Description;
            //    return value.ToString();
            //}

            ///**/
            ///// <summary>
            /////   Gets all <see cref="EnumItemDescriptionAttribute"/>
            /////   for special enum value: <paramref name="value"/> and special enum type: <paramref name="enumType"/>.
            ///// </summary>
            ///// <param name="value">the value of special enum</param>
            ///// <param name="enumType">the enum type of special enum</param>
            ///// <returns>All attributes attatched on special enum value.</returns>
            //private static Dictionary<CultureInfo, EnumItemDescriptionAttribute> GetDisplayValuesImp(object value, Type enumType)
            //{
            //    if (!_cache.ContainsKey(enumType))
            //    {
            //        Dictionary<object, Dictionary<CultureInfo, EnumItemDescriptionAttribute>> displayValues =
            //            new Dictionary<object, Dictionary<CultureInfo, EnumItemDescriptionAttribute>>();
            //        foreach (Enum item in Enum.GetValues(enumType))
            //        {
            //            Dictionary<CultureInfo, EnumItemDescriptionAttribute> descriptions =
            //                new Dictionary<CultureInfo, EnumItemDescriptionAttribute>();
            //            FieldInfo enumField = enumType.GetField(item.ToString());
            //            if (enumField != null)
            //            {
            //                foreach (EnumItemDescriptionAttribute desc
            //                    in enumField.GetCustomAttributes(typeof(EnumItemDescriptionAttribute), true))
            //                {
            //                    descriptions.Add(desc.Culture, desc);
            //                }
            //            }
            //            displayValues.Add(item, descriptions);
            //        }
            //        _cache.Add(enumType, displayValues);
            //    }

            //    return _cache[enumType][value];
            //}
        //}

        ///// <summary>
        /////   Resource reader wrapper, used to get resources by special key.
        ///// </summary>
        //private sealed class SR
        //{
        //    /// <summary>
        //    ///   A <see cref="System.Resources.ResourceManager"/>, which used to access embedded resources.
        //    /// </summary>
        //    private System.Resources.ResourceManager resources;
        //    /// <summary>
        //    ///   Singleton instance.
        //    /// </summary>
        //    private static SR loader;
        //    /// <summary>
        //    ///   Singleton instance access lock.
        //    /// </summary>
        //    private static object s_InternalSyncObject;

        //    /// <summary>
        //    ///   Constructor a new <see cref="SR"/>.
        //    /// </summary>
        //    internal SR()
        //    {
        //        //TODO: If you modified resource location, please update here...
        //        this.resources = new System.Resources.ResourceManager(
        //            string.Concat(typeof(EnumAdapter).Namespace, ".Resource"),
        //            base.GetType().Assembly);
        //    }

        //    /// <summary>
        //    ///   Get singleton instance.
        //    /// </summary>
        //    /// <returns>A singleton <see cref="SR"/></returns>
        //    private static SR GetLoader()
        //    {
        //        if (loader == null)
        //        {
        //            lock (SR.InternalSyncObject)
        //            {
        //                if (loader == null)
        //                {
        //                    loader = new SR();
        //                }
        //            }
        //        }
        //        return loader;
        //    }

        //    /// <summary>
        //    ///   Gets an object from resources by special key, which provided by <paramref name="name"/>.
        //    /// </summary>
        //    /// <param name="name">Resource accessed key</param>
        //    /// <returns>return stored object in resource. if resource not found, return <paramref name="name"/> as object.</returns>
        //    public static object GetObject(string name)
        //    {
        //        SR loader = GetLoader();
        //        if (loader == null)
        //        {
        //            return null;
        //        }
        //        try
        //        {
        //            return loader.resources.GetObject(name, Culture);
        //        }
        //        catch { }
        //        return name;
        //    }

        //    /// <summary>
        //    ///   Gets a string from resources by special key, which provided by <paramref name="name"/>.
        //    /// </summary>
        //    /// <param name="name">Resource accessed key</param>
        //    /// <returns>return stored string in resource. If resource not found, retuen <paramref name="name"/> as result.</returns>
        //    public static string GetString(string name)
        //    {
        //        SR loader = GetLoader();
        //        if (loader == null)
        //        {
        //            return null;
        //        }
        //        try
        //        {
        //            return loader.resources.GetString(name, Culture);
        //        }
        //        catch { }
        //        return name;
        //    }

        //    /// <summary>
        //    ///   Gets a formatted string from resources by special key, which provided by <paramref name="name"/> and optional parameters.
        //    /// </summary>
        //    /// <param name="name">Resource accessed key</param>
        //    /// <param name="args">format arguments.</param>
        //    /// <returns>return stored string in resource. If resource not found, use <paramref name="name"/> as formator, return the formatted string.</returns>
        //    public static string GetString(string name, params object[] args)
        //    {
        //        SR loader = GetLoader();
        //        if (loader == null)
        //        {
        //            return null;
        //        }
        //        string format = name;
        //        try
        //        {
        //            format = loader.resources.GetString(name, Culture);
        //        }
        //        catch { }

        //        if ((args == null) || (args.Length <= 0))
        //        {
        //            return format;
        //        }

        //        // It's better cut long arg for formating.
        //        for (int i = 0; i < args.Length; i++)
        //        {
        //            string arg = args[i] as string;
        //            if ((arg != null) && (arg.Length > 0x400))
        //            {
        //                args[i] = arg.Substring(0, 0x3fd) + "...";
        //            }
        //        }
        //        return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args);
        //    }

        //    /// <summary>
        //    ///   Culture to used to access resources.
        //    /// </summary>
        //    private static System.Globalization.CultureInfo Culture
        //    {
        //        get { return null; }
        //    }

        //    /// <summary>
        //    ///   Singleton sync lock.
        //    /// </summary>
        //    private static object InternalSyncObject
        //    {
        //        get
        //        {
        //            if (s_InternalSyncObject == null)
        //            {
        //                object obj2 = new object();
        //                System.Threading.Interlocked.CompareExchange(ref s_InternalSyncObject, obj2, null);
        //            }
        //            return s_InternalSyncObject;
        //        }
        //    }

        //    /// <summary>
        //    ///   Gets the <see cref="System.Resources.ResourceManager"/>
        //    /// </summary>
        //    public static System.Resources.ResourceManager Resources
        //    {
        //        get { return GetLoader().resources; }
        //    }
        //}
    }
}
